package mysql

import (
	"database/sql"
	"errors"
	"fmt"
	"gitee.com/kinwyb/godb/v2"
	_ "github.com/go-sql-driver/mysql"
	"github.com/spf13/cast"
	"github.com/xwb1989/sqlparser"
	"regexp"
	"strings"
	"time"
)

const DriverName godbv2.DriverName = "mysql"

// MYSQL mysql操作对象
type MYSQL struct {
	godbv2.Connect
	mysqlQuery
	linkString string
}

// Connect 链接mysql数据库，其中other参数代表链接字符串附加的配置信息
// 其中other="loc=Local&multiStatements=true"
//
//eg:mysql://user:password@tcp(host:port)/dbname?loc=Local&multiStatements=true
func Connect(host, username, password, db string, other ...string) (*MYSQL, error) {
	linkstring := username + ":" + password + "@tcp(" + host + ")/" + db
	if len(other) > 0 {
		linkstring += "?" + other[0]
	}
	result := &MYSQL{}
	sqlDB, err := sql.Open(string(DriverName), linkstring)
	if err != nil {
		return nil, err
	}
	sqlDB.SetConnMaxIdleTime(30 * time.Minute) //半个小时后重置链接
	result.SetSQLDB(sqlDB)
	result.SetDataBaseName(db) //记录数据库名称,表名格式化会用到
	result.Conn = sqlDB
	result.DriverName = DriverName
	result.linkString = linkstring
	return result, nil
}

func (m *MYSQL) Transaction(t godbv2.TransactionFunc, option ...*sql.TxOptions) error {
	connQuery := &mysqlQuery{}
	txConn := godbv2.NewTxConnectQuery(connQuery)
	return m.NewTransaction(t, txConn.GetTxSQL, option...)
}

func (m *MYSQL) Close() {
	m.Connect.Close()
	m.Conn = nil
}

type mysqlQuery struct {
	godbv2.ConnectQuery
}

func (m *mysqlQuery) SetConn(conn godbv2.QueryWithContext) {
	m.ConnectQuery.Conn = conn
	m.CheckConn = false
}

func (m *mysqlQuery) QueryRow(sql string, args ...interface{}) *godbv2.QueryResult {
	if m.CheckConn {
		if err := m.Connect(); err != nil {
			return godbv2.ErrQueryResult(err, m.Dbname, sql, args)
		}
	}
	stmt, err := sqlparser.Parse(sql)
	if err != nil {
		return godbv2.ErrQueryResult(fmt.Errorf("sql语句解析错误:%w", err), m.Dbname, sql, args)
	}
	switch stmt := stmt.(type) {
	case *sqlparser.Select:
		if stmt.Limit != nil {
			stmt.Limit.Rowcount = sqlparser.NewIntVal([]byte("1"))
		} else {
			stmt.SetLimit(&sqlparser.Limit{
				Rowcount: sqlparser.NewIntVal([]byte("1")),
			})
		}
	default:
		return godbv2.ErrQueryResult(errors.New("只支持select语句"), m.Dbname, sql, args)
	}
	buf := sqlparser.NewTrackedBuffer(nil)
	stmt.Format(buf)
	sql = regexp.MustCompile("(\\:v\\d+)").ReplaceAllString(buf.String(), "?")
	return m.QueryRows(sql, args...)
}

func (m *mysqlQuery) QueryWithPage(sql string, page *godbv2.PageObj, args ...interface{}) *godbv2.QueryResult {
	if page == nil {
		return m.QueryRows(sql, args...)
	}
	sqlInfo, err := godbv2.mysqlSelectSQLParser(sql)
	if err != nil {
		return godbv2.ErrQueryResult(err, m.Dbname, sql, args)
	}
	sqlBuilder := strings.Builder{}
	if page.Total < 1 {
		sqlBuilder.WriteString("SELECT count(0) num FROM ")
		sqlBuilder.WriteString(sqlInfo.table)
		sqlBuilder.WriteString(sqlInfo.where)
		sqlBuilder.WriteString(sqlInfo.groupBy)
		sqlBuilder.WriteString(sqlInfo.having)
		result := m.QueryRows(sqlBuilder.String(), args...)
		count := cast.ToInt64(cast.ToString(result.Get("num")))
		page.SetTotal(count)
		sqlBuilder.Reset()
	}
	currentpage := 0
	if page.Page-1 > 0 {
		currentpage = page.Page - 1
	}
	if page.Total < 1 {
		return godbv2.NewRowsResult(nil, sql, args)
	}
	sqlBuilder.WriteString("SELECT ")
	sqlBuilder.WriteString(sqlInfo.selectColumns)
	sqlBuilder.WriteString(" FROM ")
	sqlBuilder.WriteString(sqlInfo.table)
	sqlBuilder.WriteString(sqlInfo.where)
	sqlBuilder.WriteString(sqlInfo.groupBy)
	sqlBuilder.WriteString(sqlInfo.having)
	sqlBuilder.WriteString(sqlInfo.orderBy)
	sqlBuilder.WriteString(" LIMIT ")
	sqlBuilder.WriteString(cast.ToString(page.Rows * currentpage))
	sqlBuilder.WriteString(",")
	sqlBuilder.WriteString(cast.ToString(page.Rows))
	sql = sqlBuilder.String()
	return m.QueryRows(sql, args...)
}
